#
#  MacEverMoreAppDelegate.py
#  MacEverMore
#

from Foundation import *
from AppKit import *


from ConnectionHandler import ConnectionHandler


from PyObjCTools import AppHelper
from PyObjCTools import NibClassBuilder

class MacEverMoreAppDelegate(NibClassBuilder.AutoBaseClass):
    """
    This is the main object for the Mac application.
    """
    def awakeFromNib(self):
        """
        Perform all needed initialization. Automatically called at application
        (almost) startup.
        """
        # Fire off a thread to control the GPS.
        self._gpsProxy = ConnectionHandler(controller=self)
        self._gpsProxy.start()

        # Init the various labels.
        statusStr = 'Not connected'
        firmwareStr = 'N/A'
        usedSpaceStr = 'N/A'
        
        self.statusLabel.setStringValue_(statusStr)
        self.firmwareLabel.setStringValue_(firmwareStr)
        self.usedSpaceLabel.setStringValue_(usedSpaceStr)
        self.dataStreamLabel.setStringValue_('')

        self.transferDataButton.setEnabled_(False)
        self.eraseLogDataButton.setEnabled_(False)
        self.resetGPSButton.setEnabled_(False)

        # Some internal variables.
        self._isConnected = False
        self._numPoints = None
        return

    def applicationDidFinishLaunching_(self, notification):
        """
        Check the status of the device. Automatically called once the
        application is done starting up.
        """
        return

    def gpsDidConnect(self):
        """
        Called when the GOS is connected.
        """
        # Start the progress indicator.
        self.progressWheel.startAnimation_(self)
        
        # Check the status of the GPS.
        (firmwareVersion,
         percentUsed,
         self._numPoints) = self._gpsProxy.gpsStatus()

        # Update the various labels.
        statusStr = 'Connected'
        firmwareStr = firmwareVersion
        usedSpaceStr = '%.02f%% (~%d points)' \
                       %(percentUsed, self._numPoints)
        
        self.statusLabel.setStringValue_(statusStr)
        self.firmwareLabel.setStringValue_(firmwareStr)
        self.usedSpaceLabel.setStringValue_(usedSpaceStr)
        self.dataStreamLabel.setStringValue_('')

        # If there is no data, no point is enabling the transferData button.
        thereIsData = self._numPoints > 0
        self.transferDataButton.setEnabled_(thereIsData)
        self.eraseLogDataButton.setEnabled_(thereIsData)
        self.resetGPSButton.setEnabled_(True)
        
        # Stop the progress indicator.
        self.progressWheel.stopAnimation_(self)
        return

    def gpsDidDisconnect(self):
        """
        Called when the GPS is disconnected.
        """
        statusStr = 'Not connected'
        firmwareStr = 'N/A'
        usedSpaceStr = 'N/A'

        self.statusLabel.setStringValue_(statusStr)
        self.firmwareLabel.setStringValue_(firmwareStr)
        self.usedSpaceLabel.setStringValue_(usedSpaceStr)
        self.dataStreamLabel.setStringValue_('')

        self.transferDataButton.setEnabled_(False)
        self.eraseLogDataButton.setEnabled_(False)
        self.resetGPSButton.setEnabled_(False)
        return
        
    
    def resetGPS_(self, sender):
        """
        Erase log data stored on the device.

        Invoked by the GUI. This is the first half of the whole process.
        """
        # Create an alert saying: are you really sure???
        alert = NSAlert.alloc().init()
        alert.addButtonWithTitle_('OK')
        alert.addButtonWithTitle_('Cancel')
        alert.setMessageText_('Delete the on board memory?')
        alert.setInformativeText_('Deleted data from the GPS device cannot ' + \
                                  'be restored.')
        alert.setAlertStyle_(NSWarningAlertStyle)

        # Display the alert.
        alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(\
            self.mainWindow,
            self,
            'resetGPSAlertDidEnd:returnCode:contextInfo:',
            0)
        return

    @AppHelper.endSheetMethod
    def resetGPSAlertDidEnd_returnCode_contextInfo_(self, theSheet, retCode, ctx):
        """
        Second half: dismiss the alert and delete the onboard memory (only if
        the user clicked OK).
        """
        if(retCode != NSAlertFirstButtonReturn):
            self.statusLabel.setStringValue_('Idle')
            return
        # <-- end if

        # Close the panel.
        theSheet.window().orderOut_(self)

        # Reset the GPS.
        self._gpsProxy.resetToDefaults()

        # Update the labels.
        self.statusLabel.setStringValue_('GPS reset to default configuration')
        return

    def eraseLogData_(self, sender):
        """
        Reset the GPS to its default values.

        Invoked by the GUI. This is the first half of the whole process.
        """
        # Create an alert saying: are you really sure???
        alert = NSAlert.alloc().init()
        alert.addButtonWithTitle_('OK')
        alert.addButtonWithTitle_('Cancel')
        alert.setMessageText_('Delete the on board memory?')
        alert.setInformativeText_('Deleted data from the GPS device cannot ' + \
                                  'be restored.')
        alert.setAlertStyle_(NSWarningAlertStyle)

        # Display the alert.
        alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(\
            self.mainWindow,
            self,
            'eraseDataAlertDidEnd:returnCode:contextInfo:',
            0)
        return

    @AppHelper.endSheetMethod
    def eraseDataAlertDidEnd_returnCode_contextInfo_(self, theSheet, retCode, ctx):
        """
        Second half: dismiss the alert and delete the onboard memory (only if
        the user clicked OK).
        """
        if(retCode != NSAlertFirstButtonReturn):
            self.statusLabel.setStringValue_('Idle')
            return
        # <-- end if

        # Close the panel.
        theSheet.window().orderOut_(self)

        # Update the status label.
        self.statusLabel.setStringValue_('Deleting data...')

        # Start the progress indicator.
        self.progressWheel.startAnimation_(self)
        
        # Delete the data.
        gpxData = self._gpsProxy.eraseLogDataBuffer()

        # Stop the progress indicator.
        self.progressWheel.stopAnimation_(self)

        # Update the labels.
        self.statusLabel.setStringValue_('Data erased')

        # Somehow retrieving the memory status info never returns...
        # (percentUsed, self._numPoints) = self._gpsProxy.memoryStatus()
        percentUsed = 0.
        self._numPoints = 0
        usedSpaceStr = '%.02f%% (~%d points)' %(percentUsed, self._numPoints)
        self.usedSpaceLabel.setStringValue_(usedSpaceStr)
        return

    def transferData_(self, sender):
        """
        Copy the data over from the GPS internal memory. Save the file in GPX
        format.

        Invoked by the GUI. This is the first half of the whole process.
        """
        # Update the status label.
        self.statusLabel.setStringValue_('Downloading data...')

        # Create and customize the save panel.
        panel = NSSavePanel.savePanel()
        panel.setRequiredFileType_('gpx')
        panel.setCanSelectHiddenExtension_(True)
        
        # Display the panel.
        # res = panel.runModalForDirectory_file_(NSHomeDirectory(), '')
        panel.beginSheetForDirectory_file_modalForWindow_modalDelegate_didEndSelector_contextInfo_(\
            NSHomeDirectory(),
            '',
            self.mainWindow,
            self,
            'savePanelDidEnd:returnCode:contextInfo:',
            0)
        return

    @AppHelper.endSheetMethod
    def savePanelDidEnd_returnCode_contextInfo_(self, theSheet, retCode, ctx):
        """
        Second half: the panel is about to be dismissed. If the used clicked OK,
        retrieve the fileName and save the data to it.
        """
        if(retCode != NSOKButton):
            self.statusLabel.setStringValue_('Idle')
            return
        # <-- end if

        # Close the panel.
        theSheet.close()

        # Fetch the file name.
        fileName = theSheet.filename()

        # Start the progress indicator.
        self.progressWheel.startAnimation_(self)
        
        # Fetch the data.
        gpxData = self._gpsProxy.getLogData()

        # Save the file.
        f = file(fileName, 'w')
        f.write(gpxData)
        f.close()

        # Stop the progress indicator.
        self.progressWheel.stopAnimation_(self)

        # Update the status label.
        self.statusLabel.setStringValue_('File saved')
        return

    # Data stream.
    def displayStreamData(self, data):
        # Parse the input data (GGA message).
        tokens = data.strip().split(',')
        if(len(tokens) == 16):
            # DGPS in use.
            (msgType,
             t,
             lat,
             latDir,
             lon,
             lonDir,
             fix,
             numSat,
             hdop,
             alt,
             altUnit,
             sep,
             sepUnit,
             ageDgps,
             dgps,
             crc) = tokens
        else:
            # DGPS not in use.
            (msgType,
             t,
             lat,
             latDir,
             lon,
             lonDir,
             fix,
             numSat,
             hdop,
             alt,
             altUnit,
             sep,
             sepUnit,
             dgps,
             crc) = tokens
        # <-- end if

        # Parse the time.
        tStr = 'UTC Time %s:%s:%s' %(t[:2], t[2:4], t[4:6])
        
        if(fix == '0'):
            self.dataStreamLabel.setStringValue_(tStr + '  -  no fix')
            return
        # <-- end if

        # Parse lon and lat.
        latStr = '%s:%s:%d %s' %(lat[:2],
                                 lat[2:4],
                                 int(round(float(lat[4:]))),
                                 latDir)
        lonStr = '%s:%s:%d %s' %(lon[:3],
                                 lon[3:5],
                                 int(round(float(lon[5:]))),
                                 lonDir)
        self.dataStreamLabel.setStringValue_('%s  -  %s, %s' \
                                             %(tStr, latStr, lonStr))
        return

    # Error handling methods.
    def fatalError(self, msg):
        """
        Something really bad happened. Display an alert dialog and quit.
        """
        print(msg)
        return

    def error(self, msg):
        """
        Something not quite that bad happened. Display an alert dialog and
        continue.
        """
        print(msg)
        return

    # Main window delegare methods.
    def windowShouldClose_(self, sender):
        """
        Window Delegate method: quit the application when the main window is
        closed.
        """
        NSApp().terminate_(self)
        return(True) 
